﻿// Inner Fire 游戏引擎库
// InstancedSprite - 实例精灵绘制器
//
// Copyright (c) 2025 by 尔西大帝. All rights reserved.
//
// 所属层级：元素层（3）
// 对精灵实例进行绘制。只需要记录位置信息。其它的都是GPU计算。只有单一颜色。
//
// Author: 尔西大帝
// Email: 2670613871@qq.com
// Created: 2025-05-21

#include "instanced_sprites.h"
#include <enum_parser.h>
#include <graphics/transform_def.h>

using namespace DirectX;

namespace ifire::dx {
InstancedSprites::InstancedSprites(
    InstancedSpriteType sprite_type, DxMesh* mesh, size_t count)
    : sprite_type_(sprite_type)
    , mesh_(mesh)
    , draw_count_(0)
    , sprites_(count)
    , world(IDENTITY_4X4)
    , color(1.0f, 1.0f, 1.0f, 1.0f)
    , scale(1.0f, 1.0f, 1.0f)
    , material_(nullptr) {}

void InstancedSprites::Update(
    UploadBuffer<InstancedSpriteConstants>* main_buffer,
    UploadBuffer<InstancedSpriteData>* sprite_buffer) {
  // 仅当项目已经发生变化时才设置
  if (frame_dirty_count == 0) {
    return;
  }

  // 用于复制主项目
  InstancedSpriteConstants constants;
  XMStoreFloat4x4(&constants.world, XMMatrixTranspose(XMLoadFloat4x4(&world)));
  constants.color = color;
  constants.scale = scale;
  main_buffer->CopyData(index, constants);

  // 用于复制项目
  InstancedSpriteData data{};

  // 依次遍历项目，进行设计
  for (size_t i = 0; i < sprites_.size(); i++) {
    auto& item = sprites_[i];

    // 如果是Mesh，则是一个矩阵
    if (GetSpriteType() == InstancedSpriteType::Mesh) {
      auto position = XMLoadFloat4(&item.v1);
      auto rotation = XMLoadFloat4(&item.v2);
      auto scale = XMLoadFloat4(&item.v3);
      auto mat = XMMatrixAffineTransformation(scale, VEC0, rotation, position);
      XMStoreFloat4x4(&data.mat, XMMatrixTranspose(mat));
    }else{
      data.vec[0] = item.v1;
      data.vec[1] = item.v2;
      data.vec[2] = item.v3;
    }
    // 需要复制到object_buffer_index指针的位置
    sprite_buffer->CopyData(i, data);
  }

  // 其它的帧同样也要更新
  frame_dirty_count--;
}

void InstancedSprites::SetPosition(int index, IVec3 position) {
  sprites_[index].SetPosition(position);
}

void InstancedSprites::SetTransform(
    int index, IVec3 position, IVec4 rotation, IVec3 scale) {
  auto& item = sprites_[index];
  item.SetPosition(position);
  item.SetRotation(rotation);
  item.SetScale(scale);
}

void InstancedSprites::SetColor(int index, IVec4 color) {
  sprites_[index].SetColor(color);
}

void InstancedSprites::SetLine(int index, IVec3 start, IVec3 end) {
  sprites_[index].SetPosition(start);
  sprites_[index].SetEnd(end);
}

void InstancedSprites::SetAllColor(IVec4 color, int start, int end) {
  if (end == 0) {
    end = static_cast<int>(sprites_.size() - 1);
    if (end < 0) {
      return;
    }
  }

  for (int i = start; i <= end; i++) {
    sprites_[i].SetColor(color);
  }
}

InstancedSpriteType ParseInstancedSpriteType(ikit::sstr name) {
  static std::pair<ikit::sstr, InstancedSpriteType> enums[] = {
      {"Point", InstancedSpriteType::Point}, //
      {"Line", InstancedSpriteType::Line},   //
      {"Mesh", InstancedSpriteType::Mesh}    //
  };
  static ikit::EnumParser<InstancedSpriteType> parser(
      enums, (int)InstancedSpriteType::Mesh + 1);
  return parser.Get(name, InstancedSpriteType::Point);
}

} // namespace ifire::dx